From: Guillaume Desmottes Date: Thu, 30 Jun 2011 12:05:41 +0000 (+0200) Subject: Approve room invitations X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~8097 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=998c5f17fc95a77821115bacc75ac344b406377c;p=gnome-shell.git Approve room invitations We use to rely on Empathy for this but as we plan to allow users to be connected on IM without having Empathy running the Shell should do it now. https://bugzilla.gnome.org/show_bug.cgi?id=653740 --- diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js index 35eb58166..66bd0c58f 100644 --- a/js/ui/notificationDaemon.js +++ b/js/ui/notificationDaemon.js @@ -190,9 +190,10 @@ NotificationDaemon.prototype = { actions, hints, timeout) { let id; - // Filter out chat and presence notifications from Empathy, since we + // Filter out chat, presence and invitation notifications from Empathy, since we // handle that information from telepathyClient.js if (appName == 'Empathy' && (hints['category'] == 'im.received' || + hints['category'] == 'x-empathy.im.room-invitation' || hints['category'] == 'presence.online' || hints['category'] == 'presence.offline')) { // Ignore replacesId since we already sent back a diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js index d81b40a76..8a476011a 100644 --- a/js/ui/telepathyClient.js +++ b/js/ui/telepathyClient.js @@ -178,21 +178,65 @@ Client.prototype = { } }, - _approveChannels: function(approver, account, conn, channels, - dispatchOp, context) { - // Approve the channels right away as we are going to handle it - dispatchOp.claim_with_async(this._tpClient, - Lang.bind (this, function(dispatchOp, result) { - try { - dispatchOp.claim_with_finish(result); - this._handlingChannels(account, conn, channels); - } catch (err) { - global.logError('Failed to Claim channel: ' + err); - }})); + _displayRoomInvitation: function(conn, channel, dispatchOp, context) { + // We can only approve the rooms if we have been invited to it + let selfHandle = channel.group_get_self_handle(); + if (selfHandle == 0) { + Shell.decline_dispatch_op(context, 'Not invited to the room'); + return; + } + + let [invited, inviter, reason, msg] = channel.group_get_local_pending_info(selfHandle); + if (!invited) { + Shell.decline_dispatch_op(context, 'Not invited to the room'); + return; + } + + // Request a TpContact for the inviter + Shell.get_tp_contacts(conn, [inviter], + contactFeatures, + Lang.bind(this, this._createRoomInviteSource, channel, context, dispatchOp)); + + context.delay(); + }, + + _createRoomInviteSource: function(connection, contacts, failed, channel, context, dispatchOp) { + if (contacts.length < 1) { + Shell.decline_dispatch_op(context, 'Failed to get inviter'); + return; + } + // We got the TpContact + let source = new RoomInviteSource(dispatchOp); + Main.messageTray.add(source); + + let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]); + source.notify(notif); context.accept(); }, + _approveChannels: function(approver, account, conn, channels, + dispatchOp, context) { + let channel = channels[0]; + let [targetHandle, targetHandleType] = channel.get_handle(); + + if (targetHandleType == Tp.HandleType.CONTACT) { + // Approve private text channels right away as we are going to handle it + dispatchOp.claim_with_async(this._tpClient, + Lang.bind(this, function(dispatchOp, result) { + try { + dispatchOp.claim_with_finish(result); + this._handlingChannels(account, conn, channels); + } catch (err) { + throw new Error('Failed to Claim channel: ' + err); + }})); + + context.accept(); + } else { + this._displayRoomInvitation(conn, channel, dispatchOp, context); + } + }, + _handleChannels: function(handler, account, conn, channels, requests, user_action_time, context) { this._handlingChannels(account, conn, channels); @@ -684,3 +728,86 @@ ChatNotification.prototype = { this.source.respond(text); } }; + +function RoomInviteSource(dispatchOp) { + this._init(dispatchOp); +} + +RoomInviteSource.prototype = { + __proto__: MessageTray.Source.prototype, + + _init: function(dispatchOp) { + MessageTray.Source.prototype._init.call(this, _("Invitation")); + + this._setSummaryIcon(this.createNotificationIcon()); + + this._dispatchOp = dispatchOp; + + // Destroy the source if the channel dispatch operation is invalidated + // as we can't approve any more. + this._invalidId = dispatchOp.connect('invalidated', + Lang.bind(this, function(domain, code, msg) { + this.destroy(); + })); + }, + + destroy: function() { + if (this._invalidId != 0) { + this._dispatchOp.disconnect(this._invalidId); + this._invalidId = 0; + } + + MessageTray.Source.prototype.destroy.call(this); + }, + + createNotificationIcon: function() { + // FIXME: We don't have a 'chat room' icon (bgo #653737) use + // system-users for now as Empathy does. + return new St.Icon({ icon_name: 'system-users', + icon_type: St.IconType.FULLCOLOR, + icon_size: this.ICON_SIZE }); + } +} + +function RoomInviteNotification(source, dispatchOp, channel, inviter) { + this._init(source, dispatchOp, channel, inviter); +} + +RoomInviteNotification.prototype = { + __proto__: MessageTray.Notification.prototype, + + _init: function(source, dispatchOp, channel, inviter) { + MessageTray.Notification.prototype._init.call(this, + source, + /* translators: argument is a room name like + * room@jabber.org for example. */ + _("Invitation to %s").format(channel.get_identifier()), + null, + { customContent: true }); + this.setResident(true); + + /* translators: first argument is the name of a contact and the second + * one the name of a room. "Alice is inviting you to join room@jabber.org + * for example. */ + this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier())); + + this.addButton('decline', _("Decline")); + this.addButton('accept', _("Accept")); + + this.connect('action-invoked', Lang.bind(this, function(self, action) { + switch (action) { + case 'decline': + dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, + '', function(src, result) { + src.leave_channels_finish(result)}); + break; + case 'accept': + dispatchOp.handle_with_time_async('', global.get_current_time(), + function(src, result) { + src.handle_with_time_finish(result)}); + break; + } + this.destroy(); + })); + } +}; diff --git a/src/shell-tp-client.c b/src/shell-tp-client.c index af507bc88..018f233e5 100644 --- a/src/shell-tp-client.c +++ b/src/shell-tp-client.c @@ -101,6 +101,15 @@ shell_tp_client_init (ShellTpClient *self) /* Approver */ tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter); + /* Approve room invitations. We don't handle or observe room channels so + * just register this filter for the approver. */ + tp_base_client_take_approver_filter (TP_BASE_CLIENT (self), tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_TEXT, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, + TP_HANDLE_TYPE_ROOM, + NULL)); + /* Handler */ tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter); @@ -371,3 +380,15 @@ shell_get_contact_events (TplLogManager *log_manager, NULL, NULL, callback, NULL); } + +/* gjs doesn't allow us to craft a GError so we need a C wrapper */ +void +shell_decline_dispatch_op (TpAddDispatchOperationContext *context, + const gchar *message) +{ + GError *error = g_error_new_literal (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + message); + + tp_add_dispatch_operation_context_fail (context, error); + g_error_free (error); +} diff --git a/src/shell-tp-client.h b/src/shell-tp-client.h index 064c6abe5..b32304587 100644 --- a/src/shell-tp-client.h +++ b/src/shell-tp-client.h @@ -111,5 +111,8 @@ void shell_get_contact_events (TplLogManager *log_manager, guint num_events, GAsyncReadyCallback callback); +void shell_decline_dispatch_op (TpAddDispatchOperationContext *context, + const gchar *message); + G_END_DECLS #endif /* __SHELL_TP_CLIENT_H__ */